/*
 * This file is part of the Sx Framework Library.
 * 
 * Copyright (C) 2013 University of Colorado Denver
 * <min.choi@ucdenver.edu> <shane.transue@ucdenver.edu>
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy 
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
 * copies of the Software, and to permit persons to whom the Software is 
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in 
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
 * DEALINGS IN THE SOFTWARE.
 */
#include <sxGraphicsObject.h>

const static Sx::String DEFAULT_NAME = "NoName";
const static float DEFAULT_POINT_SELECTION_DISTANCE = 0.1f;
const static float DEFAULT_LINE_SELECTION_DISTANCE = 0.1f;

Sx::Graphics::GraphicsObject::GraphicsObject(bool visible, bool selected) {
	this->visible = visible;
	this->selected = selected;
	this->name = DEFAULT_NAME;
	this->pointSelectionDistance = DEFAULT_POINT_SELECTION_DISTANCE;
	this->lineSelectionDistance = DEFAULT_LINE_SELECTION_DISTANCE;
	this->transform = Math::Transformation::Identity();
}

Sx::Graphics::GraphicsObject::GraphicsObject(const String& name, bool visible, bool selected) {
	this->visible = visible;
	this->selected = selected;
	this->name = name;
	this->pointSelectionDistance = DEFAULT_POINT_SELECTION_DISTANCE;
	this->lineSelectionDistance = DEFAULT_LINE_SELECTION_DISTANCE;
	this->transform = Math::Transformation::Identity();
}

Sx::Graphics::GraphicsObject::GraphicsObject(const String& name, float x, float y, float z) {
	this->visible = true;
	this->selected = false;
	this->name = name;
	this->pointSelectionDistance = DEFAULT_POINT_SELECTION_DISTANCE;
	this->lineSelectionDistance = DEFAULT_LINE_SELECTION_DISTANCE;
	this->transform = Math::Transformation::Identity();
	this->transform.setPosition(x, y, z);
}

Sx::Graphics::GraphicsObject::~GraphicsObject() {}

void Sx::Graphics::GraphicsObject::setVisible(bool visible) {
	this->visible = visible;
}

void Sx::Graphics::GraphicsObject::setSelected(bool selected) {
	this->selected = selected;
}

void Sx::Graphics::GraphicsObject::setColor(const Color3f& color) {
	this->color = Color4f(color.getR(), color.getG(), color.getB(), 1.0f);
}

void Sx::Graphics::GraphicsObject::setColor(const Color4f& color) {
	this->color = color;
}

void Sx::Graphics::GraphicsObject::setColor(float r, float g, float b) {
	this->color = Color4f(r, g, b, 1.0f);
}

void Sx::Graphics::GraphicsObject::setColor(float r, float g, float b, float a) {
	this->color = Color4f(r, g, b, a);
}

void Sx::Graphics::GraphicsObject::setName(const String& name) {
	this->name = name;
}

void Sx::Graphics::GraphicsObject::hide() {
	this->visible = false;
}

void Sx::Graphics::GraphicsObject::show() {
	this->visible = true;
}

void Sx::Graphics::GraphicsObject::select() {
	this->selected = true;
}

void Sx::Graphics::GraphicsObject::deselect() {
	this->selected = false;
}

void Sx::Graphics::GraphicsObject::setPointSelectionDistance(float distance) {
	this->pointSelectionDistance = distance;
}

void Sx::Graphics::GraphicsObject::setLineSelectionDistance(float distance) {
	this->lineSelectionDistance = distance;
}

bool Sx::Graphics::GraphicsObject::addPositionX(float x) {
	this->transform.addPositionX(x);
	return true;
}

bool Sx::Graphics::GraphicsObject::addPositionY(float y) {
	this->transform.addPositionX(y);
	return true;
}

bool Sx::Graphics::GraphicsObject::addPositionZ(float z) {
	this->transform.addPositionX(z);
	return true;
}

bool Sx::Graphics::GraphicsObject::addPosition(float x, float y, float z) {
	this->transform.addPosition(x, y, z);
	return true;
}

bool Sx::Graphics::GraphicsObject::addPosition(const Eigen::Vector3f& displacement) {
	this->transform.addPosition(displacement);
	return true;
}

bool Sx::Graphics::GraphicsObject::setPositionX(float x) {
	this->transform.setPositionX(x);
	return true;
}

bool Sx::Graphics::GraphicsObject::setPositionY(float y) {
	this->transform.setPositionY(y);
	return true;
}

bool Sx::Graphics::GraphicsObject::setPositionZ(float z) {
	this->transform.setPositionZ(z);
	return true;
}

bool Sx::Graphics::GraphicsObject::setPosition(float x, float y, float z) {
	this->transform.setPosition(x, y, z);
	return true;
}

bool Sx::Graphics::GraphicsObject::setPosition(const Eigen::Vector3f& position) {
	this->transform.setPosition(position);
	return true;
}

bool Sx::Graphics::GraphicsObject::setScaleX(float x) {
	this->transform.setScaleX(x);
	return true;
}

bool Sx::Graphics::GraphicsObject::setScaleY(float y) {
	this->transform.setScaleY(y);
	return true;
}

bool Sx::Graphics::GraphicsObject::setScaleZ(float z) {
	this->transform.setScaleZ(z);
	return true;
}

bool Sx::Graphics::GraphicsObject::setScale(float x, float y, float z) {
	this->transform.setScale(x, y, z);
	return true;
}

bool Sx::Graphics::GraphicsObject::setScale(const Eigen::Vector3f& scale) {
	this->transform.setScale(scale);
	return true;
}

bool Sx::Graphics::GraphicsObject::addRotationX(float angle) {
	this->transform.addRotationX(angle);
	return true;
}

bool Sx::Graphics::GraphicsObject::addRotationY(float angle) {
	this->transform.addRotationY(angle);
	return true;
}

bool Sx::Graphics::GraphicsObject::addRotationZ(float angle) {
	this->transform.addRotationZ(angle);
	return true;
}

bool Sx::Graphics::GraphicsObject::addRotation(const Eigen::Quaternionf& rotation) {
	this->transform.addRotation(rotation);
	return true;
}

bool Sx::Graphics::GraphicsObject::setRotation(const Eigen::Quaternionf& rotation) {
	this->transform.setRotation(rotation);
	return true;
}

bool Sx::Graphics::GraphicsObject::setRotation(const Eigen::Vector3f& eulerAngles) {
	this->transform.setRotation(Math::Transformation::EulerRotation(eulerAngles.x(), eulerAngles.y(), eulerAngles.z()));
	return true;
}

bool Sx::Graphics::GraphicsObject::setRotation(float eulerAngleX, float eulerAngleY, float eulerAngleZ) {
	this->transform.setRotation(Math::Transformation::EulerRotation(eulerAngleX, eulerAngleY, eulerAngleZ));
	return true;
}

bool Sx::Graphics::GraphicsObject::rotate(const Eigen::Vector3f& eulerAngles) {
	return this->rotate(eulerAngles.x(), eulerAngles.y(), eulerAngles.z());
}

bool Sx::Graphics::GraphicsObject::rotate(float eulerAngleX, float eulerAngleY, float eulerAngleZ) {
	Eigen::Quaternionf newRot = Math::Transformation::EulerRotation(eulerAngleX, eulerAngleY, eulerAngleZ);
	Eigen::Quaternionf oldRot(this->transform.getRotation());

	this->transform.getRotation() = oldRot * newRot;
	this->transform.compile();
	return true;
}

float Sx::Graphics::GraphicsObject::getPositionX() const {
	return this->transform.getPosition().x();
}

float Sx::Graphics::GraphicsObject::getPositionY() const {
	return this->transform.getPosition().y();
}

float Sx::Graphics::GraphicsObject::getPositionZ() const {
	return this->transform.getPosition().z();
}

Eigen::Vector3f Sx::Graphics::GraphicsObject::getPosition() const {
	return this->transform.getPosition();
}

float Sx::Graphics::GraphicsObject::getScaleX() const {
	return this->transform.getScale().x();
}

float Sx::Graphics::GraphicsObject::getScaleY() const {
	return this->transform.getScale().y();
}

float Sx::Graphics::GraphicsObject::getScaleZ() const {
	return this->transform.getScale().z();
}

Eigen::Vector3f Sx::Graphics::GraphicsObject::getScale() const {
	return this->transform.getScale();
}

Eigen::Quaternionf Sx::Graphics::GraphicsObject::getRotation() const {
	return this->transform.getRotation();
}

Sx::Graphics::Color4f Sx::Graphics::GraphicsObject::getColor() const {
	return this->color;
}

Sx::Graphics::Color3f Sx::Graphics::GraphicsObject::getColorSolid() const {
	return Color3f(this->color.getR(), this->color.getG(), this->color.getB());
}

float Sx::Graphics::GraphicsObject::getPointSelectionDistance() const {
	return this->pointSelectionDistance;
}

float Sx::Graphics::GraphicsObject::getLineSelectionDistance() const {
	return this->lineSelectionDistance;
}

bool Sx::Graphics::GraphicsObject::isSelected() const {
	return this->selected;
}

bool Sx::Graphics::GraphicsObject::isVisible() const {
	return this->visible;
}

Sx::String Sx::Graphics::GraphicsObject::getName() const {
	return this->name;
}

void Sx::Graphics::GraphicsObject::transformPointToLocal(const Eigen::Vector3f& worldPoint, Eigen::Vector3f& localPoint) const {
	const Eigen::Matrix4f inv = this->transform.toMatrix4f().inverse();
	localPoint = Math::ApplyTransform(inv, worldPoint);
}

void Sx::Graphics::GraphicsObject::transformPointToGlobal(const Eigen::Vector3f& localPoint, Eigen::Vector3f& globalPoint) const {
	globalPoint = Math::ApplyTransform(this->transform.toMatrix4f(), localPoint);
}

void Sx::Graphics::GraphicsObject::transformPointToGlobal(const Eigen::Vector3d& localPoint, Eigen::Vector3d& globalPoint) const {
	globalPoint = Math::ApplyTransform(this->transform.toMatrix4f(), localPoint);
}

void Sx::Graphics::GraphicsObject::transformLineToLocal(const Eigen::Vector3f& wPoint0, const Eigen::Vector3f& wPoint1, Eigen::Vector3f& lPoint0, Eigen::Vector3f& lPoint1) const {
	const Eigen::Matrix4f inv = this->transform.toMatrix4f().inverse();
	lPoint0 = Math::ApplyTransform(inv, wPoint0);
	lPoint1 = Math::ApplyTransform(inv, wPoint1);
}

Sx::Math::Transformation Sx::Graphics::GraphicsObject::getTransformation() const {
	return this->transform;
}

Sx::Math::Transformation& Sx::Graphics::GraphicsObject::transformation() {
	return this->transform;
}

const Eigen::Matrix4f& Sx::Graphics::GraphicsObject::toMatrix4f() {
	return this->transform.toMatrix4f();
}
